SegyJp2 Viewer Version 1.0 (DRAFT VERSION)

 

Bob Courtney

Geological Survey of Canada

Natural Resources Canada

Bob.courtney@nrcan.gc.ca

 

March 30, 2009


Table of Contents

 

SegyJp2 Viewer Version 1.0 (DRAFT VERSION) 1

Table of Contents. 2

Introduction. 4

Intellectual and Property Rights. 5

Installation. 7

Removal 7

Operation. 8

Startup. 8

Viewing Files. 10

Resizing the Viewing Window.. 12

Axes Appearance: 13

Zoom and Pan. 14

Pallete. 15

Setup. 16

Interpretation. 17

Horizons. 17

Adding Horizons. 18

Adding Horizons Points. 19

Deleting Horizons Points. 19

Moving Horizons Points. 19

Inserting Horizons Points. 20

Breaking Horizons Lines. 20

Deleting  Horizons. 20

Edit  Horizons. 20

Save  Horizons. 20

Markers. 22

Show marker labels: 23

Select: 23

Add: 23

Move: 23

Delete: 23

Edit selected: 23

Delete selected: 23

Save all: 24

Section. 25

Generate auto sections: 26

Capture current window: 27

Edit current: 27

Delete current: 27

Save all: 27

Export 28

Navigation: 28

Markers. 28

Sections: 28

Horizons: 29

Projection. 29

Shape file directory. 29

Export 30

Appendix I  - XML Schema. 31

Segy Header Schema. 31

Horizon Schema. 38

Routines to Manipulate JPEG2000 Box Data. 41

Appendix 2 - Default Viewer for jp2 Files; Hyperlinks in ArcMap. 56

 

 


 

Introduction

 

SegyJp2Viewer is a Windows XP program that is used to view and interpret single-channel SGYJP2 files (SEGY files that have been converted into JPEG2000 format).   We have added a functionality to export interpreted features as shape files so that they may be added to digital map sheets compiled in  ESRI ArcMap.

 

The attempt here is to implement  a database- and internet-ready  framework that will allow researchers and others to easily exchange and archive both sonar data and their interpretations. 

 

Our experience has shown that high-resolution seismic data is cumbersome and voluminous and many standard seismic processing packages do a poor job at handling and exchanging this type of  information. In addition, most of our existing interpretations of seismic data reside on Mylar film, which become detached to the original source data; these interpretations are often lost. Even when these hand-crafted interpretations have been located, it is an arduous job to convert these interpretations to a digital form.

 

JPEG2000 encoding of seismic and sidescan  offers the capability to encode SEGY data efficiently ( typical compression of 10:1 though to 40:1), offer quick evaluation of data quality through image viewing technology and, importantly, this offers a framework for embedding value-added interpretations the data. These files may contain 100,000’s of traces efficiently without undue allocation of system resources

 

One of the notable advantages of this new approach is the multiresolution or multiscale nature of the JPEG 2000 storage framework. In representing data in a multi-resolution form,  near-optimal images of these data can be displayed as one zooms in and out with very little effort and very little load on computer resources. This feature makes the onscreen representation of the seismic data  very similar to what one would see viewing a printed seismic section, with the added advantage of course that these data and the interpretations will make them are geographically registered.

 

The program used  image/trace data stored in the image section of the JPEG2000 format with file and trace header data embedded as compressed XML data in the JPEG2000 files. Any interpretations added through this package will be stored in the file as an speparate compressed XML bundle .

 

 

.

 

 

 

The SegyJp2Viewer distribution package may be freely distributed as it contains no proprietary code. It does utilize Kakadu (www.kakadusoftware.com) , GSL (http://www.network-theory.com/gsl/)   and TChart ( www.steema.com) libraries which are distributed in compliance with each of their respective licensing agreements.

 

The product will undoubtedly evolve over time so it is prudent to contact the author to obtain the most recent version. Since this product is free, sometimes one must wait until time is available to address bugs, etc.


Note : This program  does not handle strictly image-type JPEG2000 files that have been derived from scanned analog field records. I would recommend Lizardtech’s free browser plug-in-in for these files (www.lizardtech.com).  SegyJp2Viewer does not handle files in SEGY format.

 

This viewer was developed as part of the Geoscience for Ocean Management Program as a means of viewing and disseminating high-resolution digital seismic and sidescan data collected though marine operations of the Geological Survey of Canada.

 

Intellectual and Property Rights

 

This program is the intellectual property of the Government of Canada. All rights to modify and distribute this software are retained by the Government of Canada.

 

"Intellectual Property" means all rights in respect of Confidential Information, any know‑how, inventions and improvements related to Confidential Information, including without limitation, patents, copyrights, trade secrets, trade‑marks, registered industrial designs, any applications for same and all rights therein.

 

"Confidential Information" means any information of a scientific or technical nature disclosed to, generated or acquired by the Student in the course of carrying out the Student activities defined in this Agreement, whether oral or recorded in any form or medium and whether or not protectable by copyright.

 

We allow this program to be redistributed freely in its original form. However, please send me an email if you have a copy – I’ll ensure you get on a mailing list for upgrades.

 

Contact the author, Bob Courtney, Natural Resources Canada,  1 Challenger Drive , Dartmouth Nova Scotia B2Y 4A2 , to obtain the most recent version.

 

Unfortunately we have limited capacity to support needs of outside users. We do however encourage joint research agreements to extend this framework.

 

 


Installation

 

SegyJp2Viewer is easy to install.

 

(a)    If you are within the Natural Resources Canada network, you can install it directly from a network drive. Open a window to \\s5-dar-data1\shared_software\SegyJp2_Viewer\setup.exe .

 

If you are outside GSCA  but within the NRCan network ,  try using this link: \\192.55.224.44\shared_software\SegyJp2_Viewer\setup.exe

 

 

Proceed as is typical for any Windows application installation. A Canadian Flag icon will be added to your desktop and an entry under the folder NRCan will be added to your Program list.

 

(b)    If you obtain a ZIP file containing the release, unpack the zip file to a directory on a local drive and use Explore to enter the SegyJP2 subdirectory that is unpacked into the specified spot. Double click on setup.exe and proceed as above.

 

 

Removal

 

To remove the package, choose Control Panel from the Windows Start Menu. Double click on  Add or Remove Programs.  Scroll down to SegyJp2_Viewer and click the  Remove button. Follow the prompts.

 

Note : It is necessary to manually remove an existing package before a newer version can be installed.


Operation

 

Startup

 

To start the program, double click on the desktop SegyJ2Viewer  icon or the icon within  the Program Files/NRCan menu entry.

 

If all goes well, the  Launcher window will appear of the desktop ( Fig 1).  The window will list SegyJp2 files that a researcher wishes to examine.

 

 

Figure 1 - Launcher Window of  SegyJp2Viewer

 

Press the Add button and select the desired SegyJp2 files from local or network sources. You can multiply select files from different directories and different drives. The program should accept only SegyJP2 files, so don’t worry if you choose the wrong files – they shouldn’t appear in the list. The chosen items will appear in the uppermost  listbox.

 

Hint: If you are going to do a lot of zooming and panning of a given file, it is best to make a local copy of the file rather than running the operation off a network file system or network attached storage box (NAS). The network file will work but the process may prove  slower.

 

When you single-click on any selected item, summary data are shown in the lower text box that is derived from the SEGY header info encoded in the SegyJp2 files (Fig 2)

 

Figure 2 - Single Click on a item to display info

 


 

Viewing Files

 

To view the contents of a file, double click on any file listed in the upper box.  A viewer window will appear on the screen (Fig. 3). Multiple windows may be brought up for a single file, each with its own view and settings, but only one of these windows may be active (zooming or panning) at a time due to file-locking restrictions imposed during these operations. Because  JPEG2000 is “lightweight” , most computer systems do not become bogged down when multiple windows are open.

 

Figure 3a - Viewing Window

 

If any interpretations had be added to the section the window will look like this :

Figure 3b - Viewing Window with existing interpretations

 

 

The status of any point in the section will be displayed in the upper right hand info box when a left button mouse click is pressed when over the image. The date and trace number is show as well as the two way time (TWT ) in msec. A depth is displayed assuming a water sound speed of 1500 m/s. Positions corresponding to the source locations in the SEGY trace header are also shown.

 

Figure 4 – Status Window

 


Resizing the Viewing Window

 

On first view, the image may appear distorted, however the viewing window may be resized to a more sensible aspect ratio by pulling on the main window’s corners. To help guide this resizing, Stretch Hor and Vert parameters are displayed in the status box in a box when resizing in the upper rand hand text box. These parameters reflect the image stretching that has been applied to image data that was retrieved from the SegyJp2 file.

 

When either parameter is greater than one, then the image has been compressed in that axis to fit the viewing window. If the parameter is less than one then the image has been stretched. Optimal image quality is achieved when these parameters are both close to one. High values  ( >> 1 ) of the stretch parameter may result in aliasing and yield a sub-optimal, noisy image. Try resizing the window and watch how these parameters change.

 

Once  the window is resized, the image will be regenerated. Typically the regeneration will take in the order of 0.5 sec. In Figure 5, the window has been resized and now displays a more typical seismic section.

 

Figure 5 – Viewing window resized to achieve better stretch ratios.

 

It may be necessary to zoom in on one axis (see next section) , in order to achieve acceptable imagery.


 

Axes Appearance:

 

By default, the left axis shows the two-way time in milliseconds, and the right axis gives the equivalent depth based on the 1500 m/s sound speed. The bottom axis is the sequential trace number in the input file, counting from zero for the first trace. The user can override the interval on the trace labels by entering the desired value in the numeric box.

 

If the segy traces are time-tagged, then the bottom axes can display survey time. Use the bottom axis combo box on the main page and choose “Time”. The interval between time fixes is given in minutes when this option is chosen. This option does not assume a constant trace firing rate, and these labels may be spaced unevenly if the firing rate changes during the line.

 

If the segy traces are position-tagged (i.e., data  in the source X and Y positions of the segy trace header), then the bottom axis can display cumulative distance along track.  Again, distance labels are often unevenly spaced along the bottom axis, representing speed changes in the survey vessel.

 

The axes and the grid properties may be interactively altered or hidden by using the chart editor, which can be turned on by clicking the triangle in the tool bar. This editor can change many other features of the plot, and I will leave it to the user to explore these options. For more information, I would look at the manufacturer’s website (http://www.steema.com/products/teechart/net/overview.html) for this graphics plug-in.


 

Zoom and Pan

 

Zoom In :

 

The user may zoom into an area of the image by “rubber-banding” a box, i.e., dragging  the mouse pointer from the upper left-hand corner of the desired region to the lower right hand corner while holding the left mouse button down.

 

 Note: Rubber-band mode is unavailable when horizons are being digitized. Zoom buttons should be used instead.

 

Zoom Out :

 

To zoom out to the full extent, “rubber-band” a box from the bottom right-hand corner back to the left topmost corner, the reverse of the zoom-in procedure.

 

Pan :

 

The user may pan an image by holding the right mouse button down on a selected part of the image and dragging the image to another point. As with the zoom function, the image will refresh shortly.

 

Note: Panning using the mouse button does work in horizon mode.

 

Pan and Zoom Buttons :

 

The user can shift the viewport left and right and up and down using the directional buttons, Left (<), Right ( >), Up (^), and Down (v).

 

The user may zoom in (Z) and zoom out (z) or zoom in and out along the x or y axis separately (Zx, zx, Zy, zy).

 

The amount of zoom and pan can be adjusted in the setup panel.

 

 


Pallete

 

The colour mapping of the seismic image may be manipulated by pressing the Pallete button. When this button is pressed, a grey scale legend is displayed in a floating window that will remain on top of the viewing window  (Figure 6). The colour map of the displayed image can be changed using the sliders on the right hand side of the palette bar.

 

Note: At present only grey scale mapping has be implemented.

 

 

Figure 6 - Palette Window

 

 


Setup

 

The Setup  button activates a window that allows the user to set default values for the zoom and pan buttons and for the refresh rate for regenerating images as the main window is resized or the image is zoomed or panned.

 

In Figure 7, Pan is set to 50%. In this case, the image will be shifted by half when the Left, Right, Up or Down button is pushed ( when in zoom mode).

 

Zoom is set to 50%. In this case, the Z, Zx, or Zy button will zoom in to 50% of the current window settings. If z, zx, or zy is clicked then the image will be zoomed out by 100/50 of the current window settings.

 

 

Figure 7 - Setup window

 


 

Interpretation

 

 

  This viewer allows the user to digitize three types of features:

 

  1. Horizons -
  2. Markers
  3. Sections

 

Horizons

 

Horizons are continuous line interpretations of seabed and sub-seabed interfaces. Horizons can be discontinuous with breaks; they can be complex and multi-valued.

 

To bring up the horizon panel, press the “Horizon” button on the main  window. 

 

To dismiss the panel,  toggle the “Horizon” button.

 

 

Adding Horizons

 

To add a horizon:

 

  1. Type in a name in the Horizon Name entry box or choose a name from the dropdown list.

 

  1. Optionally, a horizon description and the name of the user can also be entered.

 

  1. Choose a color for the line by pressing the colored box next to “Colour” and color palette chooser will be displayed.

 

  1. Then press the “Add” button.

 

 If no name is entered, a default name will be generated (horizon 0).

 

Note:  A list of names can be read into the drop-down menu of the Horizon Name entry box by pressing the Open HList button . This list should have be prepared using Notepad or another ASCII editor with one horizon name on each line of the file. By default, only files with a “.hor”extension will be displayed but files with other extensions can be entered by choosing “All files” in the file type entrybox  in the file selection box. One can save a list of horizon names that had been manually entered during use by pressing the Save HList button.

 

Adding Horizons Points

 

Choose the current horizon by clicking on the desired horizon in the Horizons list box.   The appropriate horizon name should be displayed along with description and color. At this stage, we have a number of options for adding, deleting, inserting, moving and breaking the line segment.

 

Choose the desired mode in the lower digitizer mode panel by selecting the appropriate radio button. By default, the digitizer should be in “add” mode.

 

Points can be added to the line segment, either by clicking successively on points in the image or by dragging the mouse across the image with the left button mouse button down.

 

I find the streaming mode difficult to control, and I prefer to enter points with a single mouse clicks.

 

Deleting Horizons Points

 

Choose the delete mode in the digitizer mode panel by clicking on the radio button, “del”.

 

When the radio button is clicked, a symbol will be drawn at each of the digitized points.

 

When the cursor is moved over these points, a red locator circle will be drawn over the closest digitized point.

 

This point can be deleted by clicking the left mouse button.

 

To some degree this process may be streamed by holding the left mouse button down and dragging the cursor across the screen where points are to be deleted. Again, this streaming mode can be a bit difficult to control.

 

Moving Horizons Points

 

Digitized, or pick, positions can be adjusted by choosing the “move” mode.

 

Again, symbols will be drawn each for the pick positions.

 

Move the cursor to the current pick position, hold the left mouse button down, and drag the point to the desired position.

 

Inserting Horizons Points

 

Choose the insert option to add points to the digitized line.

 

 When this option is chosen, a red locator circle will be drawn around the closest point to the cursor.

 

When the left mouse button is clicked, a point will be added midway between the closest point and the next point in the horizon line.

 

Breaking Horizons Lines

 

Horizon lines may be discontinuous.

 

When one has finished digitizing a line segment, a break between segments can be inserted by clicking the break button.

 

 

Deleting  Horizons

 

To delete a a horizon, choose a horizon by clicking on the name in the horizons box and click the delete button.

 

Edit  Horizons

 

To change the current horizon name, the horizon description, or the horizon color, choose the horizon in the horizons list box, and edit the desired entries, and press the edit button.

 

Save  Horizons

 

No work is saved until you press the “Save Digitize Horizons” button. When this is pressed, the changes will be stored in compressed XML in the SGYJP2 file.

 

If a mistake has been made and recognized before the save button has been pressed, the most recent stored XML data can be reread by pressing the refresh button, restoring the display to the previous save state.

 

Note : When in the horizon mode, the  use of the left button for zooming in and zooming out  is disabled as that event is used for horizon manipulation. The right mouse button can still be used for panning and the zoom and pan buttons on the Main page can still be used. The left mouse button zoom function is restored when the horizon window is dismissed.

 


Markers

 

As opposed to horizons which are line-type events, markers are point events.

 

These point events can be used to designate start and end of line, geological features, or any other such information as desired.

 

The marker form can be made visible and can be hidden by toggling the marker button on the main form.

 

Marker Box


Show marker labels:

 

This checkbox is used to turn the labels of the markers on or off.

 

The labels show the description that was entered when the market was created.

 

Select:

 

When this radio button is highlighted, a mouse click on the screen will select and highlight the closest marker.

 

Add:

 

This option is used to add markers and interpretation.

 

Enter a description in the description text box, and click on the image where you want the marker.

 

Often, if one is mapping repetitive features, the user can continue to click on the screen at different locations, and the last entered description will be used and saved with each marker.

 

Move:

 

Markers can be interactively moved on the canvas.

 

Check the move radio button and then click with your left mouse button on the desired marker. Hold the mouse button down, and drag the marker to the new position.

 

Delete:

 

Markers can be interactively deleted using the mouse click.

 

Choose this option, then move close to the desired marker, and the program will designate the closest marker as active. This marker will be deleted by clicking the left mouse button.

 

Edit selected:

 

One can change the description associated with the marker by clicking on the desired marker in the top list box, and then changing the description shown in the text box. Then click the edit selected button.

 

Delete selected:

 

Choose one or more markers in the list box, using standard Windows type selection rules, and click delete selected.

 

Save all:

 

Press this button to save the entire horizon package (including horizons and other features) to the source SGYJP2 file.

 

 

Note : When in the Marker mode, the  use of the left button for zooming in and zooming out  is disabled as that event is used for marker  manipulation. The right mouse button can still be used for panning and the zoom and pan buttons on the main window  can still be used. The left mouse button zoom function is restored when the horizon window is dismissed.

 


Section

 

The section functionality is used to segment the sonar file into areas of interest.

 

Often when conducting  regional mapping, we may not want to map individual events, but would prefer to map areas of similar events; e.g., mapping an area of sand waves, as opposed to mapping each sound wave individually. The section functionality allows us to this and more.

 

To use this section option, it is necessary to have positional information in the source sonar data.

 

Section Form

 

Left mouse button zoom and right mouse button pan are available on this window is exposed, so one can navigate within the sonar file with no restrictions.

 

Generate auto sections:

 

 

The generate auto sections button is used to segment the sonar file into sections of constant along-track length. This length interval is prescribed through the numeric input box.

 

 

The two direction buttons (<< and >>)  can be used to switch the viewport to the next or previously-defined section. Similarly, a different viewport can be chosen by double-clicking on the entry in the list box.

 

The “Hold Y”  checkbox can be used to ensure that only the horizontal axes is changed during the viewport change. This feature is useful if one  needs vertical expansion to show detail in a seismic section.

 

Capture current window:

 

This button will capture the currently defined window boundaries into the list box. The description entered into the lower text box will be saved along with the viewport.

 

As before, a previously-saved viewport can be chosen by double-clicking on the entry in the list box.

 

Edit current:

 

Select a desired section by double-clicking on the entry in the list box, and modify the description for this entry by typing in the lower text box. Then press the edit current button to save this modification.

 

Delete current:

 

Use this button to delete sections in the list box.

 

Delete all:

 

Use this button to delete the entire list.

 

Save all:

 

Use this button to save all input into the source sgyjp2 file. All modifications will be lost if this button or similar buttons in the horizon or marker windows have not been pressed before exiting.

 


Export

 

The export button on the Main window is used to export interpretations as shape files for subsequent import into GIS systems. By default, there are four separate shape files that will be generated:

 

Navigation:

 

It is assumed that positional data is contained in the original source file.

 

The XY positions along the track of the survey can be exported into a shape file.

 

By default, these XY positions are exported in five-minute segments as a multi-segment POLYLINE. The export interval can be changed by changing the nav  segment frequency numeric entry box.

 

At present, the “nav frequency” input has no effect.

 

The navigation shape file (which has the name of the original sonar file  plus “_nav”) is accompanied by a DBF (database IV) file that gives the start and end times of each line segment within the shape file. This DBF file can be opened up with Microsoft Excel or many other spreadsheet programs.

 

If the sgyjp2 viewer program has been assigned as the default program for opening jp2 files within the Windows context, the hyperlink column in the DBF file will become active when the _nav shape  file is queried with the identify tool in the ArcMap. This hyperlink will allow a user to seamlessly open up a designated sgyjp2 sonar file in the viewer from within ArcMap.

 

Markers

 

Markers will be exported as POINT-type shape files with the extension, “_mark”. The associated DBF file will have columns for Julian day, time, trace number, two way time down the section (msec), description, and hyperlink.

 

As with the navigation shape files, the identify tool in ArcMap can be used to show the section if the sgyjp2 viewer is designated the default program for opening up “.jp2” files.

 

In future, the x and y  positions and distance along track will be added as an additional column . So that the DBF file can be used as a stand-alone export format.

 

Sections:

 

The sections defined in the section window will be exported as multi-segment  polyline shape files  with the extension, “_sections”.  Each segment will be labeled in a DBF file with the start and end time, or the start and end trace number if trace time is not available, the description entered for each section, and a hyperlink to the file.

 

When this kind of file is imported ArcMap, the description of each of the sections can be used to label each line segment, providing an easy way of mapping the character of the section on an interval basis onto the digital map sheet within ArcMap.

 

Horizons:

 

Horizons are exported as a multi-segment  POLYLINE ZM shape-file type with the extension, “_hor”.  The Z. component of the file is the two way time of the horizon pick. The measure option within the shape file is not used.

 

The horizon export option allows both the picks alone to be exported or picks that have been linearly interpolated to give the value for each trace between picks. Obviously the latter option will generate larger files.

 

The horizon datum option is used to use one of the horizons is the datum, and the vertical time between a horizon and the data horizon is exported in the shape file. This option is useful, if they are errors in the trigger time delay of the sections or if the mapper is only interested in the two way time difference between horizons. Typically, the seabed is digitized along the whole section to establish a convenient datum.

 

Projection

 

 

It is not common to have precise projection and datum information contained within segy files.  This added information is assigned in the section.

 

By default, it is assumed that the positions in the segy trace headers are in the WGS 84 (or equivalently the NAD83) datum. One can choose the NAD27 option to define an older datum for the trace header position data.

 

Often data are stored in the header in UTM coordinates, and in this case, the UTM zone must be entered manually.

 

If an error is made in assigning these projection parameters, or if positional information is in a projection or datum not defined here, these parameters can be overridden or assigned within the GIS that imports the shape files.

 

Shape file directory

 

By default, the shape files are exported to the same directory containing the sonar file. This output directory can be changed by pressing the shape file directory button.

 

Export

 

Press this button to export layers that have been checked in the layers to export area of the window.

 

 

 


 

 

Appendix I  - XML Schema

 

Trace data are stored in the image part of the JPEG2000 file in a compressed, variable bit length format.

 

Segy file and trace header information and other related ancillary data (e.g., JPEG 2000 compression and bounding box data)  are stored in a user-defined box within the JPEG 2000 file as gzipped XML with the box label, “sxml”. 

 

Interpretation data are stored in a separate box as gzipped XML with the box label, “hxml”.

 

These schema are subject to change. However, effort will be made to make newer versions backwards compatible with the versions presented here.

 

The schema were created in Visual Studio 2005 and consequently, the schema may be used to generate class libraries for I/O using the utility, XSD.exe for C++, C# or Visual Basic. I believe Java and other languages offer similar functionality.

 

A class library (written in C++ for Visual Studio 2005) for manipulating boxes within JPEG 2000 files is given in Appendix 2  and is available from the author.

 

We have limited capacity to provide assistance and support to outside users.

Segy Header Schema

 

 

 

 ?xml version="1.0" encoding="utf-8"?>

<xs:schema xmlns:ns1="SEGY_rev_1" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="JPEG2000_SEGY">

    <xs:complexType>

      <xs:sequence>

        <xs:element name="FileInfo">

          <xs:complexType>

            <xs:all>

              <xs:element name="file_name" type="xs:string" maxOccurs="1" minOccurs="1" />

              <xs:element type="xs:string" name="instrument_type" maxOccurs="1" minOccurs="1" />

              <xs:element name="instrument_comments" type="xs:string" minOccurs="0" maxOccurs="1" />

              <xs:element name="jpeg2000_compression_parameters" type="JPEG2000CompressionParameters" maxOccurs="1" minOccurs="1">

              </xs:element>

              <xs:element name="bounding_box" type="BoundingBox" maxOccurs="1" minOccurs="1">

              </xs:element>

              <xs:element name="URI_x0020__to_digital_file" type="xs:anyURI" minOccurs="0" maxOccurs="1" />

              <xs:element name="URI_to_scanned_image_file" type="xs:anyURI" minOccurs="0" maxOccurs="1" />

              <xs:element name="input_byte_order" type="xs:boolean" minOccurs="1" maxOccurs="1" />

              <xs:element name="expedition_id" type="xs:string" />

              <xs:element name="NAP_metadata_profile" type="NAPMetadataProfile" minOccurs="0" maxOccurs="1" />

              <xs:element name="input_file_contents" type="FileContents" minOccurs="1" maxOccurs="1" />

              <xs:element name="expedition_comments" type="xs:string" maxOccurs="1" minOccurs="0" />

              <xs:element name="scientist" type="xs:string" minOccurs="0" maxOccurs="1" />

              <xs:element name="data_type" type="xs:string" />

              <xs:element name="transducer" type="xs:string" />

            </xs:all>

          </xs:complexType>

        </xs:element>

      </xs:sequence>

    </xs:complexType>

  </xs:element>

  <xs:complexType name="BoundingBox">

    <xs:sequence>

      <xs:element name="start_x0020_time" type="xs:dateTime" minOccurs="1" maxOccurs="1" />

      <xs:element name="end_x0020_time" type="xs:dateTime" maxOccurs="1" minOccurs="1" />

      <xs:element name="southern_latititude" type="xs:double" maxOccurs="1" minOccurs="1" />

      <xs:element name="northern_latititude" type="xs:double" maxOccurs="1" minOccurs="1" />

      <xs:element name="western_longitude" type="xs:double" maxOccurs="1" minOccurs="1" />

      <xs:element name="eastern_longitude" type="xs:double" maxOccurs="1" minOccurs="1" />

      <xs:element name="number_of_channels" type="xs:unsignedInt" maxOccurs="1" minOccurs="1" />

      <xs:element name="number_of_traces" type="xs:unsignedInt" maxOccurs="1" minOccurs="1" />

      <xs:element name="minimum_trace_time" type="xs:int" maxOccurs="1" minOccurs="1" />

      <xs:element name="maximum_trace_time_x0020_" type="xs:int" maxOccurs="1" minOccurs="1" />

      <xs:element name="number_of_samples_per_trace" type="xs:unsignedInt" />

      <xs:any />

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="BinaryFileHeader">

    <xs:sequence>

      <xs:element name="job_identification_number" type="xs:int" />

      <xs:element name="line_number" type="xs:int">

      </xs:element>

      <xs:element name="reel_number" type="xs:int" />

      <xs:element name="number_of_data_traces_per_ensemble" type="xs:short" />

      <xs:element name="number_of_auxiliary_traces_per_ensemble" type="xs:short" />

      <xs:element name="sample_interval_in_microseconds" type="xs:unsignedShort" />

      <xs:element type="xs:unsignedShort" name="sample_interval_in_usec_of_original_field_recording" />

      <xs:element name="number_of_samples_per_data_trace" type="xs:unsignedShort" />

      <xs:element name="number_of_samples_per_data_trace_for_original_field_recording" type="xs:unsignedShort" />

      <xs:element name="data_sample_format_code" type="xs:unsignedShort" />

      <xs:element name="ensemble_fold" type="xs:unsignedShort" />

      <xs:element name="trace_sorting_code" type="xs:short" />

      <xs:element name="vertical_sum_code" type="xs:short" />

      <xs:element name="sweep_frequency_at_start__x0028_Hz_x0029_" type="xs:unsignedShort" />

      <xs:element name="sweep_frequency_at_end__x0028_Hz_x0029_" type="xs:unsignedShort" />

      <xs:element name="sweep_length__x0028_ms_x0029_" type="xs:unsignedShort" />

      <xs:element name="sweep_type_code" type="xs:unsignedShort" />

      <xs:element name="trace_number_of_sweep_channel" type="xs:unsignedShort" />

      <xs:element name="sweep_trace_taper_length_in_milliseconds_at_start" type="xs:unsignedShort" />

      <xs:element name="sweep_trace_taper_length_in_milliseconds_at_end" type="xs:unsignedShort" />

      <xs:element name="taper_type" type="xs:unsignedShort" />

      <xs:element name="correlated_data_traces" type="xs:unsignedShort" />

      <xs:element name="binary_gain_recovered" type="xs:unsignedShort" />

      <xs:element name="amplitude_recovery_method" type="xs:unsignedShort" />

      <xs:element name="measurement_system" type="xs:unsignedShort" />

      <xs:element name="impulse_signal_polarity" type="xs:unsignedShort" />

      <xs:element name="vibratory_polarity_code" type="xs:unsignedShort" />

      <xs:element name="unassigned_3261_3500" minOccurs="0" maxOccurs="1">

        <xs:simpleType>

          <xs:restriction base="xs:base64Binary">

            <xs:length value="240" />

          </xs:restriction>

        </xs:simpleType>

      </xs:element>

      <xs:element name="SEG_Y_Format_Revision_Number" type="xs:unsignedShort" default="0" minOccurs="0" maxOccurs="1" />

      <xs:element name="fixed_length_trace_flag" type="xs:unsignedShort" default="0" minOccurs="0" maxOccurs="1" />

      <xs:element name="number_of_extended_Textual_files" type="xs:short" default="0" minOccurs="0" maxOccurs="1" />

      <xs:element name="reservered">

        <xs:simpleType>

          <xs:restriction base="xs:base64Binary">

            <xs:length value="94" />

          </xs:restriction>

        </xs:simpleType>

      </xs:element>

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="BinaryTrace">

    <xs:sequence>

      <xs:element name="trace_in_line" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />

      <xs:element name="trace_in_file" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />

      <xs:element name="orig_field_record" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />

      <xs:element name="trace_in_orig_field_record" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />

      <xs:element name="energy_source_point" type="xs:unsignedInt" minOccurs="0" maxOccurs="1" />

      <xs:element name="ensemble" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="trace_in_ensemble" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="trace_id_code" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="vertically_summed_traces" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="horizontally_summed_traces" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="data_use" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="distance_from_tx_to_rx" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="rx_elevation_" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="surface_elev_at_tx" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="tx_depth_below_surface" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="datum_elev_at_rx" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="datum_elev_at_tx" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="water_depth_at_tx" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="water_depth_at_rx" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="scalar_multiplier_69-70" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="scalar_multiplier_71-72" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="tx_x" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="tx_y" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="rx_x" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="rx_y" type="xs:int" minOccurs="0" maxOccurs="1" />

      <xs:element name="coordinate_units" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="weathering_velocity" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="subweathering_velocity" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="uphole_time_at_tx" type="xs:unsignedShort" minOccurs="0" maxOccurs="1 " />

      <xs:element name="uphole_time_at_rx" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="tx_static_correction_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="rx_static_correction_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="total_static_correction_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="lag_time_A" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="lag_time_B" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="delay_record_time" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="mute_start_time" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="mute_end_time" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="number_of_samples_in_trace" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sample_interval__x0028_usec_x0029_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="gain_type_of_field_instruments" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="instrument_gain_constant" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="instrument_initial_gain" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="correlated" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sweep_freq_start" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sweep_freq_end" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sweep_length__x0028_msec_x0029_" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sweep_type" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sweep_taper_length_start" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sweep_taper_length_end" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="taper_type" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="antialias_filter_frequency" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="antialias_filter_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="notch_filter_freq" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="notch_filter_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="low-cut_freq" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="high_cut_freq" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="low-cut_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="high-cut_slope" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="year_data_recorded" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="day" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="hr" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="min" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="sec" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="msec" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="time_weighting_factor" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="group_number_of_roll_sw_1" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="group_number_of_tr1" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="group_number_of_last_trace" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="gap_size" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="over_travel" type="xs:unsignedShort" minOccurs="0" maxOccurs="1" />

      <xs:element name="trace_data" minOccurs="0" maxOccurs="1">

        <xs:simpleType>

          <xs:restriction base="xs:base64Binary">

            <xs:maxLength value="64000" />

          </xs:restriction>

        </xs:simpleType>

      </xs:element>

      <xs:element name="taper" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="TriggerInterval" type="xs:short" minOccurs="0" maxOccurs="1" />

      <xs:element name="reserved">

        <xs:simpleType>

          <xs:restriction base="xs:base64Binary">

            <xs:length value="58" />

          </xs:restriction>

        </xs:simpleType>

      </xs:element>

      <xs:any />

    </xs:sequence>

    <xs:attribute name="is_reference_header" type="xs:boolean" default="false" />

    <xs:attribute name="trc_num" type="xs:int" />

  </xs:complexType>

  <xs:complexType name="TextualFileHeader">

    <xs:sequence>

      <xs:element name="text" minOccurs="40" maxOccurs="40">

        <xs:complexType>

          <xs:sequence>

            <xs:element name="ascii_string">

              <xs:simpleType>

                <xs:restriction base="xs:string">

                  <xs:length value="80" />

                </xs:restriction>

              </xs:simpleType>

            </xs:element>

          </xs:sequence>

          <xs:attribute name="line_number" type="xs:unsignedShort" />

        </xs:complexType>

      </xs:element>

      <xs:element name="is_EBCDIC" type="xs:boolean" />

    </xs:sequence>

    <xs:attribute name="extended_header_number" type="xs:unsignedByte" />

  </xs:complexType>

  <xs:complexType name="JPEG2000CompressionParameters">

    <xs:sequence>

      <xs:element name="transform_offset" type="xs:double" />

      <xs:element name="transform_gain" type="xs:double" />

      <xs:element name="transform_function" type="xs:short" />

      <xs:element name="number_of_significant_bits_in_input_stream" type="xs:unsignedByte" />

      <xs:element type="xs:float" name="number_of_bits_per_sample_in_encoding" />

      <xs:element name="rms_error_specified_in_encoding" type="xs:double" />

      <xs:element name="input_trace_bipolar" type="xs:boolean" />

      <xs:element name="significant_bits_at_top_of_word" type="xs:boolean" />

      <xs:element name="minimum_trace_delay_x0020__x0028_msec_x0029_" type="xs:int" />

      <xs:element name="padded_trace_length" type="xs:unsignedInt">

      </xs:element>

      <xs:element name="jp2000_wavelet_transform" type="xs:unsignedByte" />

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="SegyContents">

    <xs:sequence>

      <xs:element name="text_file_header" type="TextualFileHeader" minOccurs="1" />

      <xs:element name="binary_file_header" type="BinaryFileHeader" minOccurs="1" maxOccurs="1" />

      <xs:element name="binary_trace" type="BinaryTrace" minOccurs="1" maxOccurs="unbounded" />

      <xs:element name="original_trace_converted_to_envelope" type="xs:boolean" minOccurs="0" maxOccurs="1" default="false" />

      <xs:element name="envelope_exponent" type="xs:double" minOccurs="0" maxOccurs="1" default="1.0" />

      <xs:element name="time_lines_embedded_in_trace_data" type="xs:boolean" default="false" maxOccurs="1" minOccurs="0" />

      <xs:element name="horizontal_time_line_separation_msec" type="xs:double" minOccurs="0" maxOccurs="1" default="250" />

      <xs:element name="vertical_time_line_separation_sec" type="xs:double" minOccurs="0" maxOccurs="1" default="600." />

      <xs:element name="original_trace_converted_to_half_wave" type="xs:boolean" default="false" minOccurs="0" maxOccurs="1" />

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="NAPMetadataProfile">

    <xs:sequence>

      <xs:element name="to_x0020_be_x0020_defined" type="xs:string" />

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="FileContents">

    <xs:sequence>

      <xs:element name="file_type" type="xs:string" />

      <xs:element name="segy_contents" type="SegyContents" minOccurs="0" maxOccurs="1" />

      <xs:element name="xtf_contents" minOccurs="0" maxOccurs="1">

        <xs:complexType>

          <xs:sequence />

        </xs:complexType>

      </xs:element>

      <xs:element name="gsf_contents" minOccurs="0" maxOccurs="1">

        <xs:complexType>

          <xs:sequence />

        </xs:complexType>

      </xs:element>

    </xs:sequence>

  </xs:complexType>

</xs:schema>

 

 

Horizon Schema

 

Interpretations are stored in a separate box in structured XML. The intent here is to separate interpretations from the data, but keep them packaged together.

 

Although it has not yet been implemented, it is intended to offer the capacity to have multiple interpretations included with a given data set, offering the capacity to exchange and reinterpret data sets in the lab and over the Internet.

 

 

In the following schema, all X coordinates are sequential trace numbers and all Y coordinates are two-way travel times in milliseconds.

 

<?xml version="1.0" encoding="utf-8"?>

<xs:schema id="horizons" targetNamespace="http://tempuri.org/horizons.xsd" elementFormDefault="qualified" xmlns="http://tempuri.org/horizons.xsd" xmlns:mstns="http://tempuri.org/horizons.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:complexType name="horizon_x0020_pick">

    <xs:sequence>

      <xs:element name="x" type="xs:double" />

      <xs:element name="y" type="xs:double" />

      <xs:element name="line_break" type="xs:boolean" default="false" minOccurs="0" maxOccurs="1" />

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="horizon">

    <xs:sequence>

      <xs:element name="horizon_name" type="xs:string" minOccurs="1" maxOccurs="1" />

      <xs:element name="description" type="xs:string" minOccurs="0" />

      <xs:element name="color" type="xs:string" minOccurs="1" maxOccurs="1" />

      <xs:element name="picks" type="horizon_x0020_pick" minOccurs="0" maxOccurs="unbounded" />

      <xs:element name="date_x0020_created" type="xs:dateTime" minOccurs="0" />

      <xs:element name="date_x0020_modified" type="xs:dateTime" />

      <xs:element name="mapper" type="xs:string" minOccurs="0" />

    </xs:sequence>

  </xs:complexType>

  <xs:complexType name="horizon_x0020_group">

    <xs:sequence>

      <xs:element name="source_x0020_sgyjp2_x0020_file" type="xs:string" />

      <xs:element name="horizons" type="horizon" minOccurs="1" maxOccurs="unbounded" />

      <xs:element name="group_x0020_name" type="xs:string" />

      <xs:element name="point_x0020_markers" minOccurs="0" maxOccurs="unbounded">

        <xs:complexType>

          <xs:sequence>

            <xs:element name="x_x0020_time" type="xs:double" />

            <xs:element name="y_x0020_time" type="xs:double" />

            <xs:element name="trace_x0020_number" type="xs:int" />

            <xs:element name="comment" type="xs:string" />

            <xs:element name="marker" minOccurs="0" maxOccurs="1">

              <xs:complexType>

                <xs:sequence>

                  <xs:element name="size" type="xs:double" />

                  <xs:element name="color" type="xs:int" />

                  <xs:element name="type" type="xs:string" />

                </xs:sequence>

              </xs:complexType>

            </xs:element>

            <xs:element name="lat" type="xs:double" minOccurs="0" maxOccurs="1" />

            <xs:element name="lon" type="xs:double" minOccurs="0" maxOccurs="1" />

          </xs:sequence>

        </xs:complexType>

      </xs:element>

      <xs:element name="frame_x0020_description" maxOccurs="unbounded" minOccurs="0">

        <xs:complexType>

          <xs:sequence>

            <xs:element name="x_x0020_time_x0020_min" type="xs:double" />

            <xs:element name="x_x0020_time_x0020_max" type="xs:double" />

            <xs:element name="y_x0020_time_x0020_min" type="xs:double" />

            <xs:element name="y_x0020_time_x0020_max" type="xs:double" />

            <xs:element name="date_x0020_created" type="xs:dateTime" />

            <xs:element name="orginal_x0020_author" type="xs:string" />

            <xs:element name="last_x0020_date_x0020_modified" type="xs:dateTime" />

            <xs:element name="last_x0020_author" type="xs:string" />

            <xs:element name="description" type="xs:string" />

          </xs:sequence>

        </xs:complexType>

      </xs:element>

      <xs:element name="version" type="xs:string" minOccurs="0" maxOccurs="1" />

    </xs:sequence>

  </xs:complexType>

  <xs:element name="horizon_x0020_top_x0020_level">

    <xs:complexType>

      <xs:sequence>

        <xs:element name="group" type="horizon_x0020_group" minOccurs="1" maxOccurs="unbounded" />

      </xs:sequence>

    </xs:complexType>

  </xs:element>

</xs:schema>

 

Routines to Manipulate JPEG2000 Box Data

 

JPEG2000_Manage.h ( Visual Studio 2005 .NET)

 

#pragma once

using namespace System;

using namespace System::IO;

using namespace System::IO::Compression;

using namespace System::Runtime::InteropServices;

using namespace System::Xml;

using namespace System::Xml::Serialization;

using namespace System::Collections;

 

ref class  jp2_box_data

{

public:

            unsigned int position;

            unsigned int box_length;

            String^ box_label;

            Boolean xl_box;

            Boolean zero_length_box;

};

 

#define IO_BUFFER_LENGTH 5000000; // define a 5 mbyte io buffer

 

ref class JPEG2000_Manage

{

public:

            JPEG2000_Manage(void);

            property String^ filename;

            property unsigned int box_length;

            property Boolean xl_box;

            property Boolean zero_length_box;

            property FileStream^ filestream;

            property BinaryReader^ binaryreader;

            property BinaryWriter^ binarywriter;

 

            Boolean has_sxml_content(void); // check to see if it has segy formatted xml content

 

            // open up a file for read write

            Boolean open(void);  // file name preset with property filename;

            Boolean open(String^);

            Boolean open(Stream^ s);

 

            Boolean close(void);

 

            Boolean is_valid_jp2(void); // looks for "jP  " header box

 

 

            // get the next box header - returns box label as String^ ; box length is in this->box_length; returns null string if failed or EOF

            // leave the cuurent stream of file at start of box contents

            String^ get_next_box_header(void);

            String^ get_next_box_header(Stream^ s);

 

            // skip over the box contents leaving the file postion at start of next jp2000 box

            Boolean skip_box_contents(void);

            Boolean skip_box_contents(Stream^ s);

 

            // set filestream

            void go_to_top_of_file(void);

 

            // checks to see if at least one box with a given box_label exists

            bool find_box(String^ box_label);

 

 

            // fix zero length last box in file

            bool fix_zero_length_box();

 

            // write an serializable object to an xml file

            static Boolean write_object_to_xml_text_file(Object^ input_object, String^ xml_text_file_name);

            static Object^ read_object_from_xml_text_file(System::Type^ input_object_type, String^ xml_text_file_name);

 

 

            // write a serializable object to a gzipped  xml file

            static Boolean  write_object_to_xml_gzip_file(Object^ input_object, String^ xml_gzip_file_name);

            static Object^  read_object_from_xml_gzip_file(System::Type^ output_object_type, String^ xml_gzip_file_name);

            static System::Void read_object_from_xml_gzip_file(Object^ output_object, String^ xml_gzip_file_name);

 

 

            // write an xml version of an object in gzipped format to a stream

            Boolean write_object_to_xml_gzip_stream(Object^ input_object, Stream^ ms);

 

            // write an xml version of an object in text format to a stream

            Boolean write_object_to_xml_stream(Object^ input_object, Stream^ ms);

 

 

  // write serializable object in gzipped xml format to a jp2000 compliant box at the current stream position

            Boolean write_compressed_xml_box(Object^ input_object, String^ box_label, String^ gzip_xml_box_file_name);

 

            // write serializable object in gzipped xml format to a jp2000 compliant box at the current stream position

            Boolean write_compressed_xml_box(Object^ input_object, String^ box_label, Stream^ s);

 

 

 

 

            // write serializable object  in readable xml format to a jp2000 compliant box in a file

            Boolean write_xml_box(Object^ input_object, String^ box_label, String^ xml_box_file_name);

 

            // write serializable object  in readable xml format to a jp2000 compliant box at the current stream position

            Boolean write_xml_box(Object^ input_object, String^ box_label, Stream^ s);

 

 

 

            // Read an xml object from a stream whether compressed or not

            Object^ read_xml_box(System::Type^ input_object_type, String^ box_label , Stream^ s);

 

 

            // Copy a jp2000 compliant  box from one stream to another

            Boolean copy_box(Stream^ input_stream, Stream^ output_stream);

 

            // get a summary list of jp2000 boxes in current file - creates an arraylist of type jp2_box_data

            ArrayList^ get_box_data(void);

 

            // compress current jp2 file

            Boolean compress_existing_sxml_box(String^ new_file_name);

 

            // does the file contain compressed xml content

            Boolean has_compressed_sxml_content(void);

 

private:

 

            Boolean input_file_is_open;

            array<unsigned char,1>^ box_type;

            array<unsigned char,1>^ box_XL;

            void Write_Box_Header_to_MemoryStream(String ^box_label, BinaryWriter ^bw, MemoryStream ^ms);

 

 

};

 

 

 

JPEG2000_Manage.cpp ( Visual Studio 2005 .NET)

 

#include "StdAfx.h"

#include "stdio.h"

#include "JPEG2000_Manage.h"

#include "SEGY.h" // sxml header class

 

// utilities

                         System::UInt32 swap(UInt32 x)

                         {

                                     // implement a swap

                                     array<unsigned char,1>^ c =  BitConverter::GetBytes(x);

                                     unsigned char tmp;

                                     tmp = c[0];

                                     c[0] = c[3];

                                     c[3] = tmp;

 

                                     tmp = c[1];

                                     c[1] = c[2];

                                     c[2] = tmp;

 

                                     return BitConverter::ToUInt32(c,0);

 

 

                         }

                         System::UInt16 swap(UInt16 x)

                         {

                                     // implement a swap

                                     array<unsigned char,1>^ c =  BitConverter::GetBytes(x);

                                     unsigned char tmp;

                                     tmp = c[0];

                                     c[0] = c[1];

                                     c[1] = tmp;

 

                                     return BitConverter::ToUInt16(c,0);

 

 

                         }

 

                         System::Int16 swap(Int16 x)

                         {

                                     // implement a swap

                                     array<unsigned char,1>^ c =  BitConverter::GetBytes(x);

                                     unsigned char tmp;

                                     tmp = c[0];

                                     c[0] = c[1];

                                     c[1] = tmp;

 

                                     return BitConverter::ToInt16(c,0);

 

 

                         }

 

JPEG2000_Manage::JPEG2000_Manage(void)

{

            this->filestream = nullptr;

            this->binaryreader = nullptr;

            this->filename = nullptr;

            this->input_file_is_open = false;

            this->box_length = -1;

}

 

System::Boolean JPEG2000_Manage::has_sxml_content(void)

{

            return this->find_box("sxml");

}

 

System::Boolean JPEG2000_Manage::open(void)

{

            if( this->filename == nullptr) return false;

 

            this->input_file_is_open= false;

            try

            {          

                        this->filestream = File::Open(this->filename,FileMode::OpenOrCreate);

            }

            catch (System::Exception^ )

            {

                        return false;

            }

            try

            {          

                        this->binaryreader = gcnew BinaryReader(this->filestream);

            }

            catch ( System::Exception^)

            {

                        return false;

            }

                        try

            {          

                        this->binarywriter= gcnew BinaryWriter(this->filestream);

            }

            catch ( System::Exception^)

            {

                        return false;

            }

            this->input_file_is_open = true;

 

            return true;

}

System::Boolean JPEG2000_Manage::open(String^ input_name)

{

 

 

            this->filename = String::Copy(input_name); // set filename

 

            return this->open();

}

 

System::Boolean JPEG2000_Manage::open(Stream^ s)

{

 

            this->filestream = (FileStream^) s;

            try

            {          

                        this->binaryreader = gcnew BinaryReader(this->filestream);

            }

            catch ( System::Exception^)

            {

                        return false;

            }

                        try

            {          

                        this->binarywriter= gcnew BinaryWriter(this->filestream);

            }

            catch ( System::Exception^)

            {

                        return false;

            }

            this->filename = "Stream input"; // set filename

 

            return true;

}

 

 

String^ JPEG2000_Manage::get_next_box_header(void)

{

            return this->get_next_box_header(this->filestream);

}

 

String^ JPEG2000_Manage::get_next_box_header(Stream ^s)

{

                                                if ( s->Position >= s->Length ) return nullptr; // at eof

 

                                                 BinaryReader^ binaryreader = gcnew  BinaryReader(s);

                                                // read the box header bytes

                                                 // jp2 are big-endian

                                                 box_length = swap(binaryreader->ReadUInt32()) ;

 

                                                 // set zero box length flag

                                         this->zero_length_box = false;

                                                 if( !box_length)

                                                 {         

                                                             this->zero_length_box = true;

                                                             this->box_length = s->Length - s->Position + 4;

                                                 }

 

                 // read box type

                                                 box_type = binaryreader->ReadBytes(4);

                                                 this->xl_box = false;

                                                 // read next eight bytes - note first 4 bytes should be all zeroes for 32 bit file sizes

                                                 if ( box_length == 1 )

                                                 {

                                                             this->xl_box = true;

                                                             box_XL = binaryreader->ReadBytes(8);

                                                             // byte swap part of word

                                                             for ( int i = 0; i < 4 ; i++ ) {

                                                                         unsigned char tmp = box_XL[i];

                                                                         if ( tmp != NULL )

                                                                         {

                                                                                     // high bytes should be zero for 32 bit files lengths

                                                                                     return nullptr;

                                                                         }

                                                                         box_XL[i] = box_XL[7-i];

                                                                         box_XL[7-i] = tmp;

                                                             }

                                                             // limit to 32 bit lengths; adjust length for preamble bytes

                                                             box_length =Convert::ToUInt32( BitConverter::ToUInt64(box_XL,0)) ;

 

 

                                                 }

 

                                                 // copy box_type into holding array - this is a real hack!!

                                                 unsigned char tmp[5];

                                                 for ( int i = 0; i < 4; i++) tmp[i] = box_type[i];

                                                 tmp[4] = 0;

                                                 String^ name = Marshal::PtrToStringAnsi( (IntPtr)  tmp);

                                                 return name;

}

Boolean JPEG2000_Manage::close(void)

{

            this->filestream->Close();

            this->input_file_is_open = false;

            return true;

}

 

Boolean JPEG2000_Manage::is_valid_jp2(void)

{

            this->go_to_top_of_file();

 

    // make sure the first box type is jp2/jp2

            if(String::Compare(this->get_next_box_header(),"jP  ") == 0 )

            {

                        this->go_to_top_of_file();

                        return true;

            }

            return false;

}

 

void JPEG2000_Manage::go_to_top_of_file(void)

{

            if( this->input_file_is_open) this->filestream->Seek(0,SeekOrigin::Begin);

}

 

 

Boolean JPEG2000_Manage::skip_box_contents(void)

{

            return this->skip_box_contents(this->filestream);

 

}

Boolean JPEG2000_Manage::skip_box_contents(Stream^ s)

{

            // advance to end of box contents

            unsigned int new_position;

            if( this->box_length > 0 )

            {

                        new_position = s->Position +  this->box_length - 8;

                        if ( this->xl_box) new_position -= 8; // adjust skip for extra 8 bytes in xl box

 

                        if ( new_position >= s->Length)

                        {

                                    // EOF

                                    s->Position = s->Length ;

                                    return false;

 

                        } else {

                                    s->Position = new_position ;

                        }

                        return true;

            }

            else

            {

                        // box extends to end of file - zero length trace

                        // find current file position

                        int box_start = s->Position - 8;

                        s->Position = s->Length; // move to EOF

                        this->box_length = s->Position - box_start;

                        return true;

            }

 

}

 

bool JPEG2000_Manage::fix_zero_length_box()

{

            ArrayList^ boxdata = this->get_box_data();

 

 

            // last box

            this->filestream->Position = ((jp2_box_data^)boxdata[boxdata->Count - 1])->position;

 

            if ( ((jp2_box_data^)boxdata[boxdata->Count - 1])->zero_length_box )

            {

                        unsigned int swapped = swap(((jp2_box_data^)boxdata[boxdata->Count - 1])->box_length);

                        BinaryWriter^ bw = gcnew BinaryWriter(this->filestream);

                        bw->Write( swapped);

            }

 

            // done

 

            return true;

 

}

bool JPEG2000_Manage::find_box(String^ boxname)

{

            if( !this->input_file_is_open ) return false;

            if( !this->is_valid_jp2()) return false;

            // loop through boxes and box contents

            while ( String^ name = this->get_next_box_header())

            {

                        if ( String::Compare(name,boxname) == 0 )

                        {

                                    this->go_to_top_of_file();

                                    return true;

                        }

                        this->skip_box_contents();

                        if( this->filestream->Position == this->filestream->Length - 1 ) // eof condition

                        {

                                    this->go_to_top_of_file();

                                    return false;

                        }

            }

 

            return false;

}

 

// this routine writes an object to a XML text file

Boolean JPEG2000_Manage::write_object_to_xml_text_file(Object^ input_object, String^ xml_text_file_name)

{

 

                                                XmlSerializer^ ser = gcnew XmlSerializer(input_object->GetType());

                                                XmlTextWriter^ writer = gcnew XmlTextWriter( xml_text_file_name, System::Text::Encoding::UTF8);

                                                ser->Serialize(writer,input_object);

                                                writer->Close();

 

 

                                    return true;

}

Object^ JPEG2000_Manage::read_object_from_xml_text_file(System::Type^ input_object_type, String^ xml_text_file_name)

{

 

                                                Object^ output_object = nullptr;

                                                XmlSerializer^ ser = gcnew XmlSerializer(input_object_type);

                                                XmlTextReader^ reader = gcnew XmlTextReader(xml_text_file_name);

                                                output_object = ser->Deserialize(reader);

                                                reader->Close();

 

 

                                    return output_object;

}

Object^  JPEG2000_Manage::read_object_from_xml_gzip_file(System::Type^ output_object_type, String^ xml_gzip_file_name)

{

 

                        // we should be a the start of the desired box

                        // check the next two bytes if they are 1f8b then its a gzip compressed box

                Object^ output_object = nullptr;

                        FileStream^ s = gcnew FileStream(xml_gzip_file_name,System::IO::FileMode::Open);

                        XmlSerializer^ ser = gcnew XmlSerializer(output_object_type);

                        GZipStream^ gs = gcnew GZipStream(s,CompressionMode::Decompress);

                        output_object  =  ser->Deserialize(gs);

                        gs->Close();

                        s->Close();

                        return output_object;

 

}

System::Void  JPEG2000_Manage::read_object_from_xml_gzip_file(Object^ output_object, String^ xml_gzip_file_name)

{

 

                        // we should be a the start of the desired box

                        // check the next two bytes if they are 1f8b then its a gzip compressed box

                        FileStream^ s = gcnew FileStream(xml_gzip_file_name,System::IO::FileMode::Open);

                        XmlSerializer^ ser = gcnew XmlSerializer(output_object->GetType());

                        GZipStream^ gs = gcnew GZipStream(s,CompressionMode::Decompress);

                        output_object  =  ser->Deserialize(gs);

                        return;

 

}

Boolean JPEG2000_Manage::write_object_to_xml_gzip_file(Object^ input_object, String^ xml_gzip_file_name)

{

            FileStream^ fs =  nullptr;

            try

            {

                                     fs = gcnew FileStream(xml_gzip_file_name,FileMode::Create);

            }

            catch ( System::Exception^  e)

            {

                        return false;

            }

            GZipStream ^ compressedzipStream = gcnew GZipStream(fs,CompressionMode::Compress,true );

            XmlSerializer^ ser = gcnew XmlSerializer(input_object->GetType());

            XmlTextWriter^ writer = gcnew XmlTextWriter( compressedzipStream, System::Text::Encoding::UTF8);

            ser->Serialize(writer,input_object);

            writer->Close();

            compressedzipStream->Close();

            fs->Close();

            return true;

}

 

Boolean JPEG2000_Manage::write_object_to_xml_gzip_stream(Object^ input_object, Stream^ ms)

{

 

            GZipStream ^ compressedzipStream = gcnew GZipStream(ms,CompressionMode::Compress,true );

            this->write_object_to_xml_stream(input_object,compressedzipStream);

            compressedzipStream->Close();

 

            return true;

}

 

Boolean JPEG2000_Manage::write_object_to_xml_stream(Object^ input_object, Stream^ ms)

{

 

            XmlSerializer^ ser = gcnew XmlSerializer(input_object->GetType());

            XmlTextWriter^ writer = gcnew XmlTextWriter( ms, System::Text::Encoding::UTF8);

            ser->Serialize(writer,input_object);

 

 

            return true;

}

 

Boolean JPEG2000_Manage::write_compressed_xml_box(Object^ input_object, String^ box_label, String^ gzip_xml_box_file_name)

{

                                                FileStream^ ms = gcnew FileStream(gzip_xml_box_file_name,FileMode::Create);

                                                 BinaryWriter^ bw = gcnew BinaryWriter(ms);

                                                 // write 8 byte preamble to box

                                                 bw->Write((int)0);

                                                 char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(box_label);

                                                 for( int i = 0; i < 4; i++) bw->Write((unsigned char) str2[i]);

 

                                                 // write gzipped contents

                                                 this->write_object_to_xml_gzip_stream(input_object,ms);

 

                                                 // update length of box

                                                 unsigned int ms_size = ms->Length;

                                                 ms->Position = 0;

                                                 bw->Write(swap(ms_size));

 

                                                 // close up

                                                 bw->Close();

 

                                                 return true;

}

 

 

 

Boolean JPEG2000_Manage::write_xml_box(Object^ input_object, String^ box_label, String^ xml_box_file_name)

{

                                                 FileStream^ ms = gcnew FileStream(xml_box_file_name,FileMode::Create);

                                                 BinaryWriter^ bw = gcnew BinaryWriter(ms);

                                                 // write 8 byte preamble to box

                                                 bw->Write((int)0);

                                                 char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(box_label);

                                                 for( int i = 0; i < 4; i++) bw->Write((unsigned char) str2[i]);

 

                                                 // write contents .......

                                                 this->write_object_to_xml_stream(input_object,ms);

 

                                                 // update length of box

                                                 unsigned int ms_size = ms->Length;

                                                 ms->Position = 0;

                                                 bw->Write(swap(ms_size));

 

                                                 // close up

                                                 bw->Close();

 

                                                 return true;

}

 

void JPEG2000_Manage::Write_Box_Header_to_MemoryStream(String ^box_label, BinaryWriter ^bw, MemoryStream ^ms)

{

            // write preamble to stream - note that a memory stream < 2 Gbyte

            bw->Write(swap((unsigned int)(ms->Length + 8)));

            char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(box_label);

            for( int i = 0; i < 4; i++) bw->Write((unsigned char) str2[i]);

}

Boolean JPEG2000_Manage::write_xml_box(Object^ input_object, String^ box_label, Stream^ s)

{

                                                 BinaryWriter^ bw = gcnew BinaryWriter(s);

                                                 MemoryStream^ ms = gcnew MemoryStream();

 

                                                 // put text xml in a memory stream

                                                 this->write_object_to_xml_stream(input_object,ms);

 

                                                 Write_Box_Header_to_MemoryStream(box_label, bw, ms);

 

                                                // dump memory stream to parent stream

                                                 ms->WriteTo(s);

 

                                                 

                                                 return true;

}

 

Boolean JPEG2000_Manage::write_compressed_xml_box(Object^ input_object, String^ box_label, Stream^ s)

{

                                                 BinaryWriter^ bw = gcnew BinaryWriter(s);

                                                 MemoryStream^ ms = gcnew MemoryStream();

 

                                                 // put gzip xml in a memory stream

                                                 this->write_object_to_xml_gzip_stream(input_object,ms);

 

                                                 Write_Box_Header_to_MemoryStream(box_label, bw, ms);

 

                                                // dump memory stream to parent stream

                                                 ms->WriteTo(s);

 

                                                 

                                                 return true;

}

 

ArrayList^ JPEG2000_Manage::get_box_data(void)

{

            ArrayList^ r = gcnew ArrayList();

            this->go_to_top_of_file();

            while(this->filestream->Position < this->filestream->Length)

            {

                        jp2_box_data^ bd = gcnew jp2_box_data();

                        bd->position = this->filestream->Position;

                        bd->box_label = this->get_next_box_header();

                        bd->box_length = this->box_length;

                        bd->xl_box = this->xl_box;

                        bd->zero_length_box = this->zero_length_box;

                        r->Add(bd);

           

                        this->skip_box_contents();

            }

            return r;

 

}

 

// Read an xml object from a stream whether compressed or not

Object^ JPEG2000_Manage::read_xml_box(System::Type^ input_object_type, String^ box_label , Stream^ s)

{

 

                        Object^ output_object = nullptr;

 

                        while ( String::Compare(this->get_next_box_header(s), box_label) != 0 )

                        {

                                    this->skip_box_contents(s);

                                    if( s->Position >=  s->Length  )return nullptr;

                        }

 

                        // we should be a the start of the desired box

                        // check the next two bytes if they are 1f8b then its a gzip compressed box

                        BinaryReader^ br = gcnew BinaryReader(s);

                        unsigned char c1 = br->ReadByte();

                        unsigned char c2 = br->ReadByte();

                        s->Position -= 2; // back up

 

                        // read box contents

                        MemoryStream^ ms ;

                        if( this->xl_box )

                        {

                                    ms =  gcnew MemoryStream(br->ReadBytes(this->box_length-16));

                        } else

                        {

                                    ms =  gcnew MemoryStream(br->ReadBytes(this->box_length-8));

                        }

                        XmlSerializer^ ser = gcnew XmlSerializer(input_object_type);

 

                        if ( c1 == 31 && c2 == 139 )

                        {

                                    // this is gzipped xml

                                    GZipStream^ gs = gcnew GZipStream(ms,CompressionMode::Decompress);

                                    output_object  =  ser->Deserialize(gs);

                        } else {

                                    output_object  =  ser->Deserialize(ms);

                                   

                        }

                        return output_object;

 

                       

}

 

Boolean JPEG2000_Manage::copy_box(Stream^ is, Stream^ os)

{

 

            // straight copy of one box from one stream to another

 

            // read box header

            String^ is_box_label = this->get_next_box_header(is);

 

            if( this->xl_box)

            {

                        is->Position -= 16; // xl box

            } else {

                        is->Position -= 8; // back up

            }

            if ( is_box_label == nullptr ) return false;

 

            // total length of box

 

            unsigned int is_length = this->box_length;

            if( this->box_length == 0 )

            {

                        // this is the last box in the file

                        is_length = is->Length - is->Position;

            }

            // assign an io buffer;

            unsigned int buffer_length = IO_BUFFER_LENGTH;

            if( buffer_length >  is_length ) buffer_length =  is_length ;

            array<unsigned char>^ buffer = gcnew array<unsigned char>(buffer_length);

 

            // number of reads

            unsigned int nread_full = is_length/buffer_length;

            for ( int i = 0; i < nread_full; i++ )

            {

                        is->Read(buffer,0,buffer_length);

                        os->Write(buffer,0,buffer_length);

            }

           

            unsigned int nread_partial = is_length % buffer_length;

            if( nread_partial )

            {

                        is->Read(buffer,0,nread_partial);

                        os->Write(buffer,0,nread_partial);            

            }

           

            return true;

           

}

Boolean JPEG2000_Manage::has_compressed_sxml_content(void)

{

            unsigned int save_position = this->filestream->Position;

 

            ArrayList^ bd = this->get_box_data(); // get input box info

 

            // look for sxml header

            for ( int i = 0; i < bd->Count; i++ )

            {

                        jp2_box_data^ b = (jp2_box_data^)bd[i];

                        if ( String::Compare(b->box_label,"sxml") == 0 )

                        {

                                    // found sxml tag

                                    this->filestream->Position = b->position + 8;

                                    if( b->xl_box ) this->filestream->Position += 8; // xl box

                                    // next two characters are gzip magic numbers

                                    unsigned char c1 = this->binaryreader->ReadByte();

                                    unsigned char c2 = this->binaryreader->ReadByte();

                                    if ( c1 == 31 && c2 == 139 )

                                    {

                                                this->filestream->Position = save_position;

                                                return true;

                                    }

                        }

 

            }

 

            this->filestream->Position = save_position;

            return false;

}

Boolean  JPEG2000_Manage::compress_existing_sxml_box(String^ new_file_name)

{

            try

            {

                                    JPEG2000_Manage^ newjp2 = gcnew JPEG2000_Manage();

 

                                    newjp2->open(new_file_name); // open a blank file

 

                                    ArrayList^ bd = this->get_box_data(); // get input box info

 

                                    this->go_to_top_of_file(); // rewind existing file

 

                                    for ( int i = 0; i < bd->Count; i ++ ) // cycle through boxes

                                    {

                                      jp2_box_data^ bdi = (::jp2_box_data^) bd[i];

 

                                      if ( String::Compare(bdi->box_label,"sxml") == 0 )

                                      {

                                                  JPEG2000_SEGY^ jp2sgy = gcnew JPEG2000_SEGY() ;

                                                  jp2sgy = (JPEG2000_SEGY^) this->read_xml_box(jp2sgy->GetType(),"sxml",this->filestream);

 

                                                  //rewrite in a compressed format

                                                  this->write_compressed_xml_box(jp2sgy,"sxml",newjp2->filestream);

                                      } else {

                                                  this->copy_box(this->filestream, newjp2->filestream);

                                      }

                                    }

                                    // insure output file has not zero length boxes

                                    newjp2->fix_zero_length_box();

                                    newjp2->close();

            }

 

            catch (  System::Exception^ )

            {

                        return false;

            }

 

            return true;

}

 

 

Appendix 2 - Default Viewer for jp2 Files; Hyperlinks in ArcMap      

 

This application  can be made the default viewer for “.jp2” files. To do this, right click on a “.jp2” file on your desktop in an explorer window. Choose the “Open With” option followed by “Choose Program”.

 

 

Click onthe Browse button and navigate to the NRCan/SegyJp2Viewer subdirectory under Program Files. Double click on the SegyViewer.exe entry.  Then click the “Always use the….” checkbox.

 

From this point, any sgyjp2 file can be viewed by double clicking on the file in a file browser.

 

In addition, shape file withbhyperlinks pointing to “.jp2” files  in ArcMap will now use this viewer.

 

The catch : image- type jp2 files can’t be viewed with this application.